package de.koller.oceanFramework.math;

public class RVec3d {
	
	public double x, y, z;
	
	public RVec3d() {
		this.clear();
	}
	
	public RVec3d( double x, double y, double z ) {
		this.set( x, y, z );
	}
	
	public RVec3d( RVec3d o ) {
		this.set( o );
	}
	
	public final RVec3d set( RVec3d o ) {
		this.x = o.x;
		this.y = o.y;
		this.z = o.z;
		return this;
	}
	
	public final RVec3d set( double x, double y, double z ) {
		this.x = x;
		this.y = y;
		this.z = z;
		return this;
	}
	
	public final RVec3d clear() {
		this.x = 0;
		this.y = 0;
		this.z = 0;
		return this;
	}
	
	public final RVec3d add( RVec3d o ) {
		this.x += o.x;
		this.y += o.y;
		this.z += o.z;
		return this;
	}
	
	public final RVec3d sub( RVec3d o ) {
		this.x -= o.x;
		this.y -= o.y;
		this.z -= o.z;
		return this;
	}
	
	public final RVec3d mult( double s ) {
		this.x *= s;
		this.y *= s;
		this.z *= s;
		return this;
	}
	
	public final RVec3d div( double s ) {
		this.x /= s;
		this.y /= s;
		this.z /= s;
		return this;
	}
	
	public static RVec3d difference( RVec3d a, RVec3d b ) {
		return a.newInstace().sub( b );
	}
	
	public final RVec3d negate() {
		this.x = -this.x;
		this.y = -this.y;
		this.z = -this.z;
		return this;
	}
	
	public final RVec3d newInstace() {
		return new RVec3d( this );
	}
	
	@Override
	public String toString() {
		return "( " + x + ", " + y + ", " + z + " )";
	}
	
	@Override
	public int hashCode() {
		final long prime = 31;
		long result = 1;
		result = prime * result + Double.doubleToLongBits(x);
		result = prime * result + Double.doubleToLongBits(y);
		result = prime * result + Double.doubleToLongBits(z);
		return (int)result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		RVec3d other = (RVec3d)obj;
		if (Double.doubleToLongBits(x) != Double.doubleToLongBits( other.x))
			return false;
		if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
			return false;
		if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
			return false;
		return true;
	}
	
	public static double dot( RVec3d a, RVec3d b ) {
		return a.x * b.x + a.y * b.y + a.z * b.z;
	}
	
	public static RVec3d cross( RVec3d a, RVec3d b ) {
		return new RVec3d( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x );
	}
	
	public final double getLength() {
        return Math.sqrt( this.getLengthSquared() );
    }

    public final double getLengthSquared() {
        return x * x + y * y + z * z;
    }
	
	public final RVec3d normalize() {
		double length = getLengthSquared();
        if( length != 1d && length != 0d ) {
            length = 1d / Math.sqrt( length );
            return this.mult( length );
        } 
		return this;
	}
	
	public static double getAngle( RVec3d a, RVec3d b ) {
        double dotProduct = dot( a, b );
        double angle = Math.acos( dotProduct );
        return angle;
    }
	
	public final double getLength2D() {
        return Math.sqrt( this.getLengthSquared2D() );
    }

    public final double getLengthSquared2D() {
        return x * x + y * y;
    }
	
	public float getDirection2D() {
		return(float)Math.atan2( y, x );
	}
	
	public RVec3d setLength2D( double l ) {
		setTo2D( getDirection2D(), l );
		return this; 
	}
	
	public void setDirection2D( double d ) {
		setTo2D( d, getLength2D() );
	}
	
	public void setTo2D( double direction, double length ) {
		this.x = Math.cos( direction ) * length;
		this.y = Math.sin( direction ) * length;
	}
	
}